package com.yang.core.model;

import lombok.Builder;
import lombok.Data;

import java.time.Duration;

/**
 * 重试策略
 */
@Data
@Builder
public class RetryPolicy {

    /**
     * 最大尝试次数
     */
    private int maxAttempts;

    /**
     * 初始延迟
     */
    private Duration initialDelay;

    /**
     * 退避倍数
     */
    private double backoffMultiplier;

    /**
     * 最大延迟
     */
    private Duration maxDelay;

    /**
     * 重试条件
     */
    private RetryCondition retryCondition;

    /**
     * 创建默认重试策略
     */
    public static RetryPolicy defaultPolicy() {
        return RetryPolicy.builder()
                .maxAttempts(3)
                .initialDelay(Duration.ofMillis(1000))
                .backoffMultiplier(2.0)
                .maxDelay(Duration.ofSeconds(30))
                .retryCondition(RetryCondition.defaultCondition())
                .build();
    }

    /**
     * 创建指数退避策略
     */
    public static RetryPolicy exponentialBackoff(int maxAttempts, Duration initialDelay) {
        return RetryPolicy.builder()
                .maxAttempts(maxAttempts)
                .initialDelay(initialDelay)
                .backoffMultiplier(2.0)
                .maxDelay(Duration.ofSeconds(60))
                .retryCondition(RetryCondition.defaultCondition())
                .build();
    }

    /**
     * 创建固定延迟策略
     */
    public static RetryPolicy fixedDelay(int maxAttempts, Duration delay) {
        return RetryPolicy.builder()
                .maxAttempts(maxAttempts)
                .initialDelay(delay)
                .backoffMultiplier(1.0)
                .maxDelay(delay)
                .retryCondition(RetryCondition.defaultCondition())
                .build();
    }

    /**
     * 重试条件接口
     */
    public interface RetryCondition {
        boolean shouldRetry(Exception exception, int attempt);

        static RetryCondition defaultCondition() {
            return (exception, attempt) -> true;
        }

        static RetryCondition maxAttempts(int maxAttempts) {
            return (exception, attempt) -> attempt < maxAttempts;
        }

        static RetryCondition retryOn(Class<? extends Exception>... exceptionTypes) {
            return (exception, attempt) -> {
                for (Class<? extends Exception> type : exceptionTypes) {
                    if (type.isInstance(exception)) {
                        return true;
                    }
                }
                return false;
            };
        }
    }
}